﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Controls;
using System.Windows.Input;
using System.Drawing;

namespace Seym.Kontroller
{
    /// <summary>
    /// Interaction logic for KutuYigini.xaml
    /// </summary>
    public partial class KutuYigini : UserControl
    {
        public event EventHandler SecimDegisti;
        public event EventHandler KutuSilindi;

        private Boyut _buyukluk;
        private List<Koordinat> _secililer = new List<Koordinat>();
        private Stack<Gecmis> _gecmis = new Stack<Gecmis>(); // Geri alma için

        public Boyut Buyukluk
        {
            get{ return _buyukluk; }
            set
            {
                _buyukluk = value;
                KutucuklariDiz();
            }
        }

        public bool HamleKaldiMi
        {
            get
            {
                return HamleKontrol();
            }
        }

        public int[] RenkSayisi { get; private set; }
        public int SeciliPuan { get; private set; }
        public int SeciliKutu { get { return _secililer.Count; } }
        public Size KutucukBoyutu { get; set; }
        public double KutucukBosluk { get; set; }
        public byte KutucukCesitSayisi { get; set; }
        public bool PuanGostergeci { get; set; }
        public bool GeriAlmaCezasi { get; set; }
        public int KutuSayisi
        {
            get
            {
                int i = 0;
                foreach (StackPanel sp in AnaStackPanel.Children)
                    foreach (Kutucuk k in sp.Children)
                        i++;
                return i;
            }
        }

        public KutuYigini()
        {
            InitializeComponent();
            RenkSayisi = new int[Enum.GetNames(typeof(KutuTurleri)).Length + 1];

            KutucukBoyutu = new Size(34, 34);
            KutucukBosluk = 1;
            KutucukCesitSayisi = 5;
            GeriAlmaCezasi = false;
            Buyukluk = Boyut.Kucuk;
        }
        /// <summary>
        /// En son gerçekleştirilen hamleyi geri alır ve geri alınan hamlenin puanını döndürür. 
        /// Geri alınacak hamle yoksa 0 döndürür
        /// </summary>
        /// <returns>Geri alınan hamlenin puan değeri</returns>
        public int HamleGeriAl()
        {
            int puan = 0;
            if (_gecmis.Count > 0)
            {
                KeyValuePair<int, StackPanel> kolon;
                Gecmis g = _gecmis.Pop();
                Kutucuk k;
                puan = PuanHesapla(g.SilinenKutular.Count);
                if (GeriAlmaCezasi)
                    puan += (int)((1.0 / (double)g.SilinenKutular.Count) * puan);

                while (g.SilinenKolonlar.Count > 0)
                {
                    kolon = g.SilinenKolonlar.Pop();
                    AnaStackPanel.Children.Insert(kolon.Key, kolon.Value);
                }
                while (g.SilinenKutular.Count > 0)
                {
                    k = g.SilinenKutular.Pop();
                    // Kutuyu eski haline döndür
                    k.SeciliMi = false;
                    k.SilinecekMi = false;
                    RenkSayisi[k.Koordinat.Tur]++;
                    // sonra yerine koy
                    ((StackPanel)AnaStackPanel.Children[k.Koordinat.X]).Children.Insert(k.Koordinat.Y, k);
                }
                KoordinatDuzenle();
            }
            return puan;
        }

        private static int PuanHesapla(int kutuSayisi)
        {
            return (int)Math.Pow(kutuSayisi, 2);
        }

        /// <summary>
        /// Kutucukları, Buyukluk özelliğindeki değere ve renk sayısına göre oluşturup ilk kullanıma hazırlar
        /// </summary>
        public void KutucuklariDiz()
        {
            StackPanel dikeyStackPanel;
            Random rnd = new Random();
            Koordinat koor;

            // temizlik
            AnaStackPanel.Children.Clear();
            _gecmis.Clear();

            for (int i = 0; i < RenkSayisi.Length; i++)
                RenkSayisi[i] = 0;

            for (int i = 0; i < _buyukluk.Genislik; i++)
            {
                dikeyStackPanel = new StackPanel();
                dikeyStackPanel.Height = (KutucukBoyutu.Height + (KutucukBosluk * 2.0)) * _buyukluk.Yukseklik;

                Kutucuk k;
                for (int j = 0; j < _buyukluk.Yukseklik; j++)
                {
                    koor = new Koordinat()
                    {
                        Tur = (byte)rnd.Next(1, KutucukCesitSayisi + 1),
                        X = i,
                        Y = j
                    };
                    RenkSayisi[koor.Tur]++;

                    k = new Kutucuk();
                    k.Koordinat = koor;
                    k.Cursor = Cursors.Hand;
                    k.MouseLeftButtonUp += new MouseButtonEventHandler(kutu_MouseLeftButtonUp);

                    dikeyStackPanel.Children.Add(k);
                }

                AnaStackPanel.Children.Add(dikeyStackPanel);
            }
            AnaStackPanel.Width = (KutucukBoyutu.Width + (KutucukBosluk * 2.0)) * _buyukluk.Genislik;
        }

        /// <summary>
        /// Tüm kutuların seçim durumunu kaldırır
        /// </summary>
        private void TumKutulariSifirla()
        {
            for (int x = 0; x < _buyukluk.Genislik; x++)
                for (int y = 0; y < _buyukluk.Yukseklik; y++)
                    KutucukBul(x, y).SeciliMi = false;
        }

        /// <summary>
        /// Verilen koordinatlardaki kutunun nesnesini döndürür
        /// </summary>
        /// <param name="x">X koordinatı</param>
        /// <param name="y">Y koordinatı</param>
        /// <returns>Eğer verilen (x,y) koordinatlarında Kutucuk var ise onu döndürür; yok ise BosKutu dondurur</returns>
        private Kutucuk KutucukBul(int x, int y)
        {
            if (AnaStackPanel.Children.Count <= x || ((StackPanel)AnaStackPanel.Children[x]).Children.Count <= y)
                return Kutucuk.BosKutu;
            else
                return (Kutucuk)((StackPanel)AnaStackPanel.Children[x]).Children[y];
        }

        /// <summary>
        /// Verilen koordinatlardaki kutunun nesnesini döndürür
        /// </summary>
        /// <param name="k">Bulunmak istenen kutucuğun koordinat nesnesi</param>
        /// <returns>Eğer verilen (x,y) koordinatlarında Kutucuk var ise onu döndürür; yok ise BosKutu dondurur</returns>
        private Kutucuk KutucukBul(Koordinat k)
        {
            return KutucukBul(k.X, k.Y);
        }

        /// <summary>
        /// Silinecek olarak işaretlenmiş kutuları siler
        /// </summary>
        private void KutucukSil()
        {
            StackPanel sp;
            Kutucuk k;
            Gecmis g = new Gecmis();
            for (int x = AnaStackPanel.Children.Count - 1; x >= 0; x--)
            {
                sp = AnaStackPanel.Children[x] as StackPanel;
                for (int y = sp.Children.Count - 1; y >= 0; y--)
                {
                    k = (Kutucuk)sp.Children[y];
                    if (k.SilinecekMi)
                    {
                        RenkSayisi[k.Koordinat.Tur]--;
                        g.SilinenKutular.Push(k);
                        sp.Children.Remove(k);
                    }
                }
                if (sp.Children.Count == 0)
                {
                    AnaStackPanel.Children.Remove(sp);
                    g.SilinenKolonlar.Push(new KeyValuePair<int, StackPanel>(x, sp));
                }
            }
            _gecmis.Push(g);
        }

        /// <summary>
        /// Kutucuklar silindikten sonra değişen koordinatları düzenler
        /// </summary>
        private void KoordinatDuzenle()
        {
            StackPanel sp;
            Kutucuk k;
            for (int x = 0; x < AnaStackPanel.Children.Count; x++)
            {
                sp = AnaStackPanel.Children[x] as StackPanel;
                for (int y = 0; y < sp.Children.Count; y++)
                {
                    k = (Kutucuk)sp.Children[y];
                    k.Koordinat = new Koordinat(x, y, k.Koordinat.Tur);
                }
            }
        }

        /// <summary>
        /// Daha oynanacak hamle var mı diye kontrol eder
        /// </summary>
        private bool HamleKontrol()
        {
            StackPanel sp;
            Kutucuk k;
            Koordinat eleman;

            for (int x = 0; x < AnaStackPanel.Children.Count; x++)
            {
                sp = AnaStackPanel.Children[x] as StackPanel;
                for (int y = 0; y < sp.Children.Count; y++)
                {
                    eleman = KutucukBul(x, y).Koordinat;
                    foreach (Koordinat item in Komsular(eleman))
                    {
                        if (0 <= item.X && item.X < AnaStackPanel.Children.Count && 0 <= item.Y && item.Y < sp.Children.Count) // Matrix sınırları içinde mi
                        {
                            k = KutucukBul(item);
                            if (eleman.Tur == k.Koordinat.Tur) // Şu anki kutucuk ile aynı tür olan komşular
                                return true;
                        }
                    }
                }
            }

            return false;
        }

        /// <summary>
        /// Verilen koordinatın sağındaki, solundaki, üstündeki ve altındaki 4 kutunun Koordinat değerini döndürür
        /// </summary>
        /// <param name="el">Komşuları bulunacak koordinat</param>
        /// <returns></returns>
        private static Koordinat[] Komsular(Koordinat el)
        {
            return new Koordinat[] { 
                new Koordinat(el.X - 1,el.Y),
                new Koordinat(el.X + 1,el.Y),
                new Koordinat(el.X, el.Y - 1),
                new Koordinat(el.X, el.Y + 1 )
            };
        }

        private void BenzerKomsular(Koordinat eleman)
        {
            if (_secililer.Contains(eleman, KoordinatComparer.GetComparer())) // daha önce eklenmiş mi?
                return;

            _secililer.Add(eleman);

            foreach (Koordinat item in Komsular(eleman))
            {
                Kutucuk k;
                if (0 <= item.X && item.X < AnaStackPanel.Children.Count && 0 <= item.Y && item.Y < ((StackPanel)AnaStackPanel.Children[item.X]).Children.Count) // Matrix sınırları içinde mi
                { 
                    k = KutucukBul(item);
                    if (eleman.Tur == k.Koordinat.Tur) // Şu anki kutucuk ile aynı tür olan komşular
                        BenzerKomsular(k.Koordinat);
                }
            }
        }

        #region Olay Tetikleyiciler

        protected void OnSecimDegisti()
        {
            if (SecimDegisti != null)
                SecimDegisti(this, EventArgs.Empty);
        }

        protected void OnKutuSilindi()
        {
            if (KutuSilindi != null)
                KutuSilindi(this, EventArgs.Empty);
        }

        #endregion

        private void kutu_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
        {
            Kutucuk kutu = (Kutucuk)sender;

            if (kutu.SeciliMi)
            {
                for (int i = 0; i < _secililer.Count; i++)
                {
                    Kutucuk k = KutucukBul(_secililer[i]);
                    k.SilinecekMi = true;
                }
                KutucukSil();
                KoordinatDuzenle();

                if (PuanGostergeci)
                {
                    PuanLabelPopup.IsOpen = true;
                    PuanLabel.Text = string.Format("+ {0}", SeciliPuan);
                    PuanLabel.Kay();
                }
                OnKutuSilindi();
                _secililer.Clear();
                SeciliPuan = 0;
                OnSecimDegisti();
            }
            else
            {
                _secililer.Clear();
                BenzerKomsular(kutu.Koordinat);
                TumKutulariSifirla();

                if (_secililer.Count > 1)
                {
                    for (int i = 0; i < _secililer.Count; i++)
                        KutucukBul(_secililer[i]).SeciliMi = true;

                    SeciliPuan = PuanHesapla(_secililer.Count);
                }
                else
                    _secililer.Clear();
                OnSecimDegisti();
            }
        }

        private void AnaStackPanel_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
        {
            if (e.OriginalSource == AnaStackPanel)
            {
                TumKutulariSifirla();

                _secililer.Clear();
                SeciliPuan = 0;

                OnSecimDegisti();
            }
        }

        private void PuanLabel_KaymaTamamlandi(object sender, EventArgs e)
        {
            PuanLabelPopup.IsOpen = false;
        }

        private class Gecmis
        {
            public Stack<Kutucuk> SilinenKutular { get; set; }
            public Stack<KeyValuePair<int, StackPanel>> SilinenKolonlar { get; set; }

            public Gecmis()
            {
                SilinenKutular = new Stack<Kutucuk>();
                SilinenKolonlar = new Stack<KeyValuePair<int, StackPanel>>();
            }
        }
    }
}
